using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;

using Yammer;

using Behemoth.SharePoint.WebParts;

namespace Behemoth.SharePoint.Web.YammerAPI {
  
  public enum YammerAuthStatus {
    None = 0,
    AuthObjectCreated = 1,
    AuthObjectExists = 2,
    AccessFailed = 3,
    AccessSuccess = 4,
    SettingsStored = 5,
    UsingStoredSettings = 6
  }

  public class YammerWebToolkit {

    public YammerWebToolkit(string key, string secret) {
      this.ConsumerKey = key;
      this.ConsumerSecret = secret;
    }

    public const string CACHEKEY_YAMMER_AUTH = "YammerAuth";

    private TimeSpan _webCacheExpires = new TimeSpan(0, 5, 0); // 1 hour sliding expiration
    // TODO make time to expire configurable
    private Cache _webCache = System.Web.HttpContext.Current.Cache;

    private string _key;
    public string ConsumerKey {
      get { return _key; }
      private set { _key = value; }
    }

    private string _secret;
    public string ConsumerSecret {
      get { return _secret; }
      private set { _secret = value; }
    }

    private YammerAuthStatus _status = YammerAuthStatus.None;
    public YammerAuthStatus Status {
      get { return _status; }
      private set { _status = value; }
    }

    /// <summary>
    /// Auth object is stored in the server web cache for a few minutes
    /// to give the user a chance to authorize the Settings object if
    /// that was not done previously.
    /// </summary>
    public Yammer.Auth Auth {
      get {
        string key = GetCacheKey(CACHEKEY_YAMMER_AUTH);
        return _webCache[key] as Yammer.Auth;
      }
      set {
        string key = GetCacheKey(CACHEKEY_YAMMER_AUTH);
        if (_webCache[key] != null) {
          if (value == null)
            _webCache.Remove(key);
          else
            _webCache[key] = value;
        } else {
          if (value == null)
            return;
          _webCache.Add(key, value, null, Cache.NoAbsoluteExpiration, _webCacheExpires, CacheItemPriority.High, null);
        }
      }
    }



    private void SetRequestToken() {
      // TODO implement use of SecureString class to store these values
      WebProxy proxy = WebProxy.GetDefaultProxy();
      Yammer.Auth auth = Yammer.Auth.GetRequestToken(proxy, this.ConsumerKey, this.ConsumerSecret);
      this.Auth = auth;
    }

    private static bool GetAccessToken(Yammer.Auth auth) {
      if (auth != null) {
        auth.GetAccessToken(); // TODO what changes does this make to Settings?
        return auth.Success;
      }
      return false;
    }

    private static string GetCacheKey(string prefix) {
      string key = prefix;
      string userId = System.Web.HttpContext.Current.User.Identity.Name;
      if (string.IsNullOrEmpty(userId))
        key += "_" + userId;
      return key;
    }

    // TODO we should try to seperate the logic form the presentation here
    public bool RenderAccessRequestLink(HtmlTextWriter writer) {
      if (this.Auth == null)
        return false;
      string url = this.Auth.Authorize(null, false);
      writer.Write("Please allow the Yammer Web Part to access Yammer on your behalf. To do so, ");
      writer.Write("<a target=\"_blank\" href=\"");
      writer.Write(url);
      writer.Write("\">click here</a>. When you have finished, Enter the security code in the input box below and click the button.");
      return true;
    }

    public void DoAuthTokenCheck(ErrorBag errors) {
      //Settings = Yammer.Settings.CheckConfiguration();
      if (Settings != null) {
        Status = YammerAuthStatus.UsingStoredSettings;
        // access to Settings property will basically do something similar to CheckConfiguration
        return;
      }
      if (this.Auth == null) {
        Status = YammerAuthStatus.AuthObjectCreated;
        SetRequestToken(); // sets this.Auth
      } else {
        Status = YammerAuthStatus.AuthObjectExists;
        GetAccessToken(this.Auth);
        if (this.Auth.Success) {
          Status = YammerAuthStatus.AccessSuccess;
          errors.AddInfo("Authentication success.");
          Settings = this.Auth.Settings;
          this.Auth = null;
          Status = YammerAuthStatus.SettingsStored;
        } else {
          Status = YammerAuthStatus.AccessFailed;
          // TODO I would *really* like to see better information returned from the API here
          // TODO any other way we can determine why we failed here?
          errors.AddWarning("Failed to authenticate. You may still need to grant this application access. ");
          SetRequestToken(); // resets token
        }
      }
    }

    /// <summary>
    /// Destroys the settings object so we can start over
    /// </summary>
    /// <param name="resetAuthObject">If true, kills the auth object associated with the request amd replaces it with a new one</param>
    public void ResetSettings(bool resetAuthObject) {
      Settings = null;
      Yammer.Settings.ClearCache();
      if (resetAuthObject)
        ResetAuthObject(false);
    }

    public void ResetAuthObject(bool makeItNull) {
      if (makeItNull) {
        this.Auth = null;
        return;
      }
      Status = YammerAuthStatus.AuthObjectCreated;
      SetRequestToken(); // sets this.Auth
    }

    public bool CleanCacheForNewPage(Page page) {
      if (!page.IsPostBack && this.IsSettingsNull && this.Auth != null) {
        ResetAuthObject(true);
        return true;
      }
      return false;
    }

    public static bool IsAuthorizationException(Exception ex) {
      return (ex.Message.Contains("Error retrieving web response") && ex.Message.Contains("(401) Unauthorized"));
    }

    #region User Profile Storage Module

    private YammerPropertyCreator _propertyCreator;

    private void EnsureProperties() {
      if (_propertyCreator == null)
        _propertyCreator = new YammerPropertyCreator();
      _propertyCreator.EnsureUserProfileProperties();
      // TODO ensure this user has a profile
    }

    private Yammer.Settings _yammerSettings = null;
    protected Yammer.Settings Settings {
      get {
        if (_yammerSettings == null) {
          EnsureProperties();
          _yammerSettings = _propertyCreator.LoadSettings();
          if (_yammerSettings == null || _yammerSettings.OAuth.TokenKey == null || _yammerSettings.OAuth.TokenSecret == null)
            _yammerSettings = null;
        }
        return _yammerSettings;
      }
      set {
        EnsureProperties();
        _yammerSettings = value;
        _propertyCreator.SaveSettings(value);
      }
    }

    public bool IsSettingsNull {
      get { return this.Settings == null; }
    }

    #endregion


    private WebProxy GetProxy() {
      if (this.Settings == null)
        throw new ArgumentNullException("Settings", "There was no Yammer settings object.");
      WebProxy proxy = null;
      if (this.Settings.Proxy.Enable) {
        proxy = new System.Net.WebProxy();
        proxy.Address = new Uri(this.Settings.Proxy.Address + ":" + this.Settings.Proxy.Port);
        proxy.Credentials = new NetworkCredential(this.Settings.Proxy.Id, this.Settings.Proxy.Password);
      }
      return proxy;
    }

    public Yammer.Session GetSession() {
      if (this.Settings == null)
        throw new ArgumentNullException("Settings", "There was no Yammer settings object.");
      OAuth.OAuthKey key = new OAuth.OAuthKey(this.ConsumerKey, this.ConsumerSecret, this.Settings.OAuth.TokenKey, this.Settings.OAuth.TokenSecret);
      WebProxy proxy = GetProxy();
      Yammer.Session session = new Yammer.Session(key, proxy);
      return session;
    }

    public void WriteMessages(HtmlTextWriter writer) {
      Yammer.Session session = GetSession();
      if (session == null)
        throw new ArgumentNullException("session", "Call did not return a valid Yammer session.");
      WriteMessages(writer, session);
    }
    public void WriteMessages(HtmlTextWriter writer, Yammer.Session session) {
      List<Yammer.Message> messages = Yammer.ApiWrapper.GetFollowingMessages(session);
      WriteMessages(writer, messages);
    }
    public void WriteMessages(HtmlTextWriter writer, List<Yammer.Message> messages) {
      writer.Write("<table class=\"YammerMessages\">");
      bool isFirst = true;
      foreach (Yammer.Message msg in messages) {
        string body = msg.Body.Plain;
        // TODO better more configurable way to localize and display times??
        DateTime globalTime = DateTime.Parse(msg.CreatedAt);
        string timeStamp = string.Format("{0} {1}", globalTime.ToShortDateString(), globalTime.ToShortTimeString());

        Yammer.User sender = null;
        Yammer.Guide guide = null;
        Yammer.Message repliedToMessage = null;
        Yammer.User repliedToUser = null;

        //if message sent from user store user information
        if (msg.SenderType.ToUpper() == Yammer.SenderType.USER.ToString())
          sender = msg.References.Users.Find(delegate(Yammer.User u) { return u.Id == msg.SenderId.ToString(); });
        //if message sent from guide store guid information
        if (msg.SenderType.ToUpper() == Yammer.SenderType.SYSTEM.ToString())
          guide = msg.References.Guide;
        //if message is a reply, store replied-to-message
        if (msg.RepliedToId != null && msg.RepliedToId != string.Empty)
          repliedToMessage = msg.References.Messages.Find(delegate(Yammer.Message m) { return m.Id == msg.RepliedToId; });
        //if reply-to-message exists, store replied-to-user
        if (repliedToMessage != null)
          repliedToUser = msg.References.Users.Find(delegate(Yammer.User u) { return int.Parse(u.Id) == repliedToMessage.SenderId; });

        if (isFirst) {
          isFirst = false;
        } else {
          // TODO find a better way to do styling
          writer.Write("<tr><td colspan=\"2\">&nbsp;</td></tr>");
        }

        if (sender != null) {
          writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\">{1}</td></tr>", "Sender", sender.FullName));
          if (!string.IsNullOrEmpty(sender.MugshotUrl))
            writer.Write(string.Format("<tr><td class=\"ms-vb\"></td><td class=\"ms-vb\"><img src=\"{0}\" /></td></tr>", sender.MugshotUrl));
        }
        if (guide != null) {
          writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\">{1}</td></tr>", "Guide:", guide.FullName));
          if (!string.IsNullOrEmpty(guide.MugshotUrl))
            writer.Write(string.Format("<tr><td class=\"ms-vb\"></td><td class=\"ms-vb\"><img src=\"{0}\" /></td></tr>", guide.MugshotUrl));
        }
        if (repliedToUser != null) {
          writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\">{1}</td></tr>", "In Reply To:", repliedToUser.FullName));
          if (!string.IsNullOrEmpty(repliedToUser.MugshotUrl))
            writer.Write(string.Format("<tr><td class=\"ms-vb\"></td><td class=\"ms-vb\"><img src=\"{0}\" /></td></tr>", repliedToUser.MugshotUrl));
        }
        writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\">{1}</td></tr>", "Date/Time:", timeStamp));
        writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\">{1}</td></tr>", "Message:", body));

        //Write attachments
        if (msg.Attachments.Count > 0) {
          foreach (Yammer.Attachment attachment in msg.Attachments) {
            writer.Write(string.Format("<tr><td class=\"ms-vb\">{0}</td><td class=\"ms-vb\"><a href=\"{2}\">{1}</a></td></tr>", "Attachment:", attachment.Name, attachment.WebUrl));
          }
        }

      } // foreach
    }

    public void PostMessage(string input, Yammer.Session session) {
      //parse message
      string pattern = "-m\\s\"+.+?\"";
      System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(pattern, System.Text.RegularExpressions.RegexOptions.Singleline);
      System.Text.RegularExpressions.Match message = regex.Match(input);
      string body = string.Empty;
      if (message != null)
        body = message.Value.Replace("-m", "").Trim().Replace("\"", "");

      //parse attachments
      pattern = "-a\\s\"+.+?\"";
      regex = new System.Text.RegularExpressions.Regex(pattern, System.Text.RegularExpressions.RegexOptions.Singleline);
      List<string> attachmentList = new List<string>();
      if (regex.IsMatch(input)) {
        message = regex.Match(input);
        attachmentList.AddRange(message.Value.Replace("-a", "").Trim().Replace("\"", "").Split(';'));
      }

      //post message
      Yammer.ApiWrapper.PostMessage(body, session, attachmentList);
    }
    public void PostMessage(string input) {
      Yammer.Session session = GetSession();
      if (session == null)
        throw new ArgumentNullException("session", "Call did not return a valid Yammer session.");
      PostMessage(input, session);
    }

  } // class
} // namespace
